package com.ke.rule.base;

import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;

import java.util.Map;
import java.util.Stack;

/**
 * 功能部件适配器, 实现部件规格的getter/setter
 * 
 * @author guyu
 */
@Component
public abstract class FuncUnitAdapter implements IFuncUnit {
  // 功能部件都是单态的，为保证线程安全，将部件规格放在ThreadLocal中
  // 在Thread-Safe的前提下，不同ApplyTo同一个部件也可能会相互调用，还要保证嵌套调用的安全
  private static final ThreadLocal<Stack<FuncUnitSpec>> localSpec = new ThreadLocal<Stack<FuncUnitSpec>>() {
    @Override
    protected Stack<FuncUnitSpec> initialValue() {
      return new Stack<FuncUnitSpec>();
    }
  };

  private FuncUnitSpec getSpec() {
    Stack<FuncUnitSpec> stack = localSpec.get();
    return stack.isEmpty() ? null : stack.peek();
  }

  /**
   * 同一个线程内的压栈，需要避免重复调用
   * @param spec
   */
  public void setSpec(FuncUnitSpec spec) {
    if (null != spec) {
      if (!this.getClass().getSimpleName().equals(spec.getName())) {
        throw new IllegalArgumentException(String.format("illegal FuncUnitSpec(actual: %s, expected: %s)",
            this.getClass().getSimpleName(), spec.getName()));
      }
    }
    localSpec.get().push(spec);
  }
  
  /**
   * 弹栈回收
   */
  public void dispose() {
    Stack<FuncUnitSpec> stack = localSpec.get();
    if (!stack.isEmpty()) {
      stack.pop();
    }
  }

  @Override
  public Integer getPriorityNbr() {
    return getSpec().getPriorityNbr();
  }

  @Override
  public ApplyTo getApplyTo() {
    return getSpec().getApplyTo();
  }

  @Override
  public Map<String, String> getWithParams() {
    return getSpec().getWithParams();
  }

  @Override
  public String[] getApplyToFieldNames(String bindableName) {
    ApplyTo applyTo = getApplyTo();
    if (null == applyTo) {
      return null;
    }
    
    Map<String, String> whichInKv = applyTo.getWhichInKv();
    if (null == whichInKv || whichInKv.isEmpty()) {
      return null;
    }
    
    if (StringUtils.isBlank(bindableName)) {
      if (1 != whichInKv.size()) {
        throw new IllegalArgumentException("There are multiple binding mappings(" + whichInKv.keySet() + "), please specify which bindable to get");
      }
      bindableName = whichInKv.keySet().iterator().next();
    }
    
    String mappingFields = whichInKv.get(bindableName);
    return StringUtils.isBlank(mappingFields) ? null : mappingFields.split("\\|");
  }

  @SuppressWarnings("unchecked")
  @Override
  public <T> T getActualParameterValue(String parameterName) {
    // TODO 根据形参类型对参数值进行转换
    return (T) getWithParams().get(parameterName);
  }
  
}
